package com.medusa.gruul.payment.service.handler.merchant;

import cn.hutool.core.util.StrUtil;
import com.medusa.gruul.common.model.enums.PayType;
import com.medusa.gruul.common.redis.util.RedisUtil;
import com.medusa.gruul.common.web.handler.Handler;
import com.medusa.gruul.payment.api.entity.MerchantDetails;
import com.medusa.gruul.payment.api.entity.PaymentMerchantConfig;
import com.medusa.gruul.payment.service.common.annotation.MerchantHandler;
import com.medusa.gruul.payment.service.model.PayConst;
import com.medusa.gruul.payment.service.model.dto.MerchantDetailsDTO;
import com.medusa.gruul.payment.service.model.enums.PaymentError;
import com.medusa.gruul.payment.service.mp.service.IMerchantDetailsService;
import com.medusa.gruul.payment.service.mp.service.IPaymentMerchantConfigService;
import lombok.RequiredArgsConstructor;
import org.springframework.core.annotation.AnnotationUtils;

import java.util.List;
import java.util.stream.Collectors;

/**
 * 商户处理器
 *
 * @author xiaoq
 * @ Description 商户抽象处理器
 * @date 2022-07-13 14:00
 */
@RequiredArgsConstructor
public abstract class AbstractMerchantHandler implements Handler<Void> {

    protected final IMerchantDetailsService merchantDetailsService;
    protected final IPaymentMerchantConfigService paymentMerchantConfigService;
    private volatile PayType payType;

    @Override
    public Void handle(Object... params) {
        if (this.hasErrorParam(params, MerchantDetailsDTO.class)) {
            throw new IllegalArgumentException("param is not MerchantDetailsDTO");
        }
        MerchantDetailsDTO config = this.cast(params[0], MerchantDetailsDTO.class);
        //同一种支付渠道、平台 只允许配置一次
        RedisUtil.lockRun(
                RedisUtil.getLockKey(RedisUtil.key(PayConst.PAY_CHANNEL_LOCK_KEY, getPayType())),
                () -> {
                    boolean notEmptyDetailId = StrUtil.isNotBlank(config.getDetailsId());
                    List<MerchantDetails> details = merchantDetailsService.lambdaQuery()
                            .select(MerchantDetails::getDetailsId)
                            .ne(notEmptyDetailId, MerchantDetails::getDetailsId, config.getDetailsId())
                            .eq(MerchantDetails::getPayType, getPayType())
                            .list();
                    if (!details.isEmpty()) {
                        List<PaymentMerchantConfig> platforms = paymentMerchantConfigService.lambdaQuery()
                                .select(PaymentMerchantConfig::getPlatform)
                                .ne(notEmptyDetailId, PaymentMerchantConfig::getDetailsId, config.getDetailsId())
                                .in(PaymentMerchantConfig::getPlatform, config.getPlatforms())
                                .in(PaymentMerchantConfig::getDetailsId, details.stream().map(MerchantDetails::getDetailsId).toList())
                                .list();
                        if (!platforms.isEmpty()) {
                            throw PaymentError.PAYMENT_CHANNEL_DUPLICATE.dataEx(platforms.stream().map(PaymentMerchantConfig::getPlatform).collect(Collectors.toSet()));
                        }
                    }
                    this.handle(config);
                }
        );

        return null;
    }


    /**
     * 商户抽象方法 渠道配置
     *
     * @param merchantDetails 支付商户渠道配置DTO
     */
    protected abstract void handle(MerchantDetailsDTO merchantDetails);

    protected void paymentMerchantConfigSave(MerchantDetailsDTO payMerchant) {
        paymentMerchantConfigService.lambdaUpdate().eq(PaymentMerchantConfig::getDetailsId, payMerchant.getDetailsId()).remove();
        payMerchant.getPlatforms().forEach(platform -> {
            PaymentMerchantConfig paymentMerchantConfig = new PaymentMerchantConfig();
            paymentMerchantConfig.setDetailsId(payMerchant.getDetailsId());
            paymentMerchantConfig.setPlatform(platform);
            paymentMerchantConfigService.save(paymentMerchantConfig);
        });
    }


    private PayType getPayType() {
        if (payType != null) {
            return payType;
        }
        synchronized (this) {
            if (payType == null) {
                MerchantHandler annotation = AnnotationUtils.findAnnotation(this.getClass(), MerchantHandler.class);
                if (annotation == null) {
                    throw new IllegalArgumentException("MerchantHandler annotation is null");
                }
                payType = annotation.value();
            }
        }
        return payType;
    }
}
